package com.wngbms.feigndemo.aop;

import com.alibaba.ttl.TransmittableThreadLocal;
import com.wngbms.feigndemo.annotation.ToBeTracedRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

@Slf4j
@Aspect
@Component
public class TraceAspectJ {

//    @Pointcut("execution(public * *.submit(java.lang.Runnable))")
//    private void submitRunnable() {
//    }
//
//    @Around("submitRunnable()")
//    public Object aroundSubmit(ProceedingJoinPoint joinPoint) throws Throwable {
//        Object[] args = joinPoint.getArgs();
//
//        Runnable runnable = (Runnable) args[0];
//        String traceId = MDC.get("traceId");
//        if (traceId != null) {
//            ProxyRunnable proxyRunnable = new ProxyRunnable(traceId, runnable);
//            Runnable pr = (Runnable) Proxy.newProxyInstance(runnable.getClass().getClassLoader(), new Class[]{Runnable.class}, proxyRunnable);
//            args[0] = pr;
//            return joinPoint.proceed(args);
//        } else {
//            return joinPoint.proceed();
//        }
//    }

    TransmittableThreadLocal<String> threadLocal = new TransmittableThreadLocal<>();

    @Around("@annotation(com.wngbms.feigndemo.annotation.ToBeTracedRequest)")
    public void setTraceId(ProceedingJoinPoint pj) {
        String traceId = null;

        try {
            MethodSignature methodSignature = (MethodSignature) pj.getSignature();
            methodSignature.getParameterNames();
            Method method = methodSignature.getMethod();
            ToBeTracedRequest annotation = method.getAnnotation(ToBeTracedRequest.class);
            String fieldName = annotation.fieldName();
            int entityIndex = annotation.entityIndex();
            Object[] args = pj.getArgs();

            if (args != null) {
                if (entityIndex < args.length) {
                    Object arg = args[0];
                    Field field = arg.getClass().getDeclaredField(fieldName);
                    field.setAccessible(true);
                    Object o = field.get(arg);
                    traceId = (String) o;
                }
            }
        } catch (Exception e) {
            log.error("get traceId exp", e);
        }

        try {
            if (traceId != null) {
                MDC.put("traceId", traceId);
                threadLocal.set(traceId);
            }
            pj.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        } finally {
            MDC.clear();
            threadLocal.remove();
        }
    }

}
